/*
 * The contents of this file are subject to the Mozilla Public License
 * Version 1.1 (the "License"); you may not use this file except in
 * compliance with the License. You may obtain a copy of the License at
 * http://www.mozilla.org/MPL/
 *
 * Software distributed under the License is distributed on an "AS IS"
 * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
 * License for the specific language governing rights and limitations
 * under the License.
 * 
 * The Original Code is iSQL-Viewer, A Mutli-Platform Database Tool.
 *
 * The Initial Developer of the Original Code is iSQL-Viewer, A Mutli-Platform Database Tool.
 * Portions created by Mark A. Kobold are Copyright (C) 2000-2007. All Rights Reserved.
 *
 * Contributor(s): 
 *  Mark A. Kobold [mkobold <at> isqlviewer <dot> com].
 *  
 * If you didn't download this code from the following link, you should check
 * if you aren't using an obsolete version: http://www.isqlviewer.com
 */
package org.isqlviewer.sql;

import java.sql.CallableStatement;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.sql.SQLWarning;
import java.sql.Savepoint;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Map;

import org.isqlviewer.util.BasicUtilities;
import org.isqlviewer.util.LocalMessages;

/**
 * TODO Add ConnectionWrapper Overview JavaDoc.
 * <p>
 * 
 * @author Mark A. Kobold &lt;mkobold at isqlviewer dot com&gt;
 * @version 1.0
 */
class ConnectionWrapper implements Connection {

    static final String BUNDLE_NAME = "org.isqlviewer.sql.ResourceBundle";
    private static final LocalMessages messages = new LocalMessages(BUNDLE_NAME);
    // Maximum for retrying creating a connection before giving up.
    private static final int CONN_MAX_RETRY = 5;
    // Delay between retries for creating a connection.
    private static final int CONN_RETRY_INTERVAL = 1000;
    private Hashtable<String, Statement> stmtMap = new Hashtable<String, Statement>();
    private Hashtable<String, CallableStatement> procMap = new Hashtable<String, CallableStatement>();
    private int holdability = 0;
    private String catalog = null;
    private boolean isClosed = false;
    private long lastAccess = 0;
    private Connection proxy = null;
    private DatabaseMetadataWrapper metaData = null;
    private StatementWrapper theStatement = null;
    private ArrayList<Statement> openStatements = new ArrayList<Statement>();
    private JdbcService dataSource = null;

    public ConnectionWrapper(JdbcService dataSource, Connection conn) {

        this.proxy = conn;
        this.dataSource = dataSource;
    }

    @Override
    public int hashCode() {

        return proxy.hashCode();
    }

    public Statement createStatement() throws SQLException {

        guardConnection();
        if (theStatement == null) {
            try {
                theStatement = new StatementWrapper(this, proxy.createStatement(), null);
            } catch (SQLException sqle) {
                error(messages.format("DataSource.GeneralMethodError", "createStatement()"), sqle);
                throw sqle;
            } catch (Throwable t) {
                SQLException sqle = createWrappedSQLException(t, "createStatement()");
                throw sqle;
            }
        }
        return theStatement;
    }

    public Statement createStatement(int resultSetType, int resultSetConcurrency) throws SQLException {

        guardConnection();
        try {
            Statement stmt = proxy.createStatement(resultSetType, resultSetConcurrency);
            return new StatementWrapper(this, stmt, null);
        } catch (SQLException sqle) {
            error(messages.format("DataSource.GeneralMethodError", "createStatement(int,int)"), sqle);
            throw sqle;
        } catch (Throwable t) {
            SQLException sqle = createWrappedSQLException(t, "createStatement(int,int)");
            throw sqle;
        }
    }

    public Statement createStatement(int resultSetType, int resultSetConcurrency, int resultSetHoldability)
            throws SQLException {

        guardConnection();
        try {
            Statement stmt = proxy.createStatement(resultSetType, resultSetConcurrency, resultSetHoldability);
            return new StatementWrapper(this, stmt, null);
        } catch (SQLException sqle) {
            error(messages.format("DataSource.GeneralMethodError", "createStatement(int,int,int)"), sqle);
            throw sqle;
        } catch (Throwable t) {
            SQLException sqle = createWrappedSQLException(t, "createStatement(int,int,int)");
            throw sqle;
        }
    }

    public CallableStatement prepareCall(String sql) throws SQLException {

        guardConnection();
        CallableStatement stmt = procMap.get(sql);
        if (stmt == null) {
            try {
                stmt = proxy.prepareCall(sql);
            } catch (SQLException sqle) {
                error(messages.format("DataSource.GeneralMethodError", "prepareCall(String)"), sqle);
                throw sqle;
            } catch (Throwable t) {
                SQLException sqle = createWrappedSQLException(t, "prepareCall(String)");
                throw sqle;
            }

            stmt = new CallableStatementWrapper(this, stmt, sql);
            synchronized (procMap) {
                procMap.put(sql, stmt);
            }
        }
        return stmt;
    }

    public CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency) throws SQLException {

        guardConnection();
        try {
            CallableStatement stmt = proxy.prepareCall(sql, resultSetType, resultSetConcurrency);
            return new CallableStatementWrapper(this, stmt, sql);
        } catch (SQLException sqle) {
            error(messages.format("DataSource.GeneralMethodError", "prepareCall(String,int,int)"), sqle);
            throw sqle;
        } catch (Throwable t) {
            SQLException sqle = createWrappedSQLException(t, "prepareCall(String,int,int)");
            throw sqle;
        }
    }

    public CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency,
            int resultSetHoldability) throws SQLException {

        guardConnection();
        try {
            CallableStatement stmt = proxy.prepareCall(sql, resultSetType, resultSetConcurrency, resultSetHoldability);
            return stmt;
        } catch (SQLException sqle) {
            error(messages.format("DataSource.GeneralMethodError", "prepareCall(String,int,int,int)"), sqle);
            throw sqle;
        } catch (Throwable t) {
            SQLException sqle = createWrappedSQLException(t, "prepareCall(String,int,int,int)");
            throw sqle;
        }
    }

    public PreparedStatement prepareStatement(String sql) throws SQLException {

        guardConnection();
        PreparedStatement stmt = (PreparedStatement) stmtMap.get(sql);
        if (stmt == null) {
            try {
                stmt = proxy.prepareStatement(sql);
            } catch (SQLException sqle) {
                error(messages.format("DataSource.GeneralMethodError", "prepareStatement(String)"), sqle);
                throw sqle;
            } catch (Throwable t) {
                SQLException sqle = createWrappedSQLException(t, "prepareStatement(String)");
                throw sqle;
            }
            stmt = new PreparedStatementWrapper(this, stmt, sql);
            synchronized (stmtMap) {
                stmtMap.put(sql, stmt);
            }
        }
        return stmt;
    }

    public PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency)
            throws SQLException {

        guardConnection();
        try {
            PreparedStatement stmt = proxy.prepareStatement(sql, resultSetType, resultSetConcurrency);
            return stmt;
        } catch (SQLException sqle) {
            error(messages.format("DataSource.GeneralMethodError", "prepareStatement(String,int,int)"), sqle);
            throw sqle;
        } catch (Throwable t) {
            SQLException sqle = createWrappedSQLException(t, "prepareStatement(String,int,int)");
            throw sqle;
        }
    }

    public PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency,
            int resultSetHoldability) throws SQLException {

        guardConnection();
        try {
            PreparedStatement stmt = proxy.prepareStatement(sql, resultSetType, resultSetConcurrency,
                    resultSetHoldability);
            return stmt;
        } catch (SQLException sqle) {
            error(messages.format("DataSource.GeneralMethodError", "prepareStatement(String,int,int,int)"), sqle);
            throw sqle;
        } catch (Throwable t) {
            SQLException sqle = createWrappedSQLException(t, "prepareStatement(String,int,int,int)");
            throw sqle;
        }
    }

    public PreparedStatement prepareStatement(String sql, int autoGeneratedKeys) throws SQLException {

        guardConnection();
        try {
            PreparedStatement stmt = proxy.prepareStatement(sql, autoGeneratedKeys);
            return stmt;
        } catch (SQLException sqle) {
            error(messages.format("DataSource.GeneralMethodError", "prepareStatement(String,int)"), sqle);
            throw sqle;
        } catch (Throwable t) {
            SQLException sqle = createWrappedSQLException(t, "prepareStatement(String,int)");
            throw sqle;
        }
    }

    public PreparedStatement prepareStatement(String sql, int[] columnIndexes) throws SQLException {

        guardConnection();
        try {
            PreparedStatement stmt = proxy.prepareStatement(sql, columnIndexes);
            return stmt;
        } catch (SQLException sqle) {
            error(messages.format("DataSource.GeneralMethodError", "prepareStatement(String,int[])"), sqle);
            throw sqle;
        } catch (Throwable t) {
            SQLException sqle = createWrappedSQLException(t, "prepareStatement(String,int[])");
            throw sqle;
        }
    }

    public PreparedStatement prepareStatement(String sql, String[] columnNames) throws SQLException {

        guardConnection();
        try {
            PreparedStatement stmt = proxy.prepareStatement(sql, columnNames);
            return stmt;
        } catch (SQLException sqle) {
            error(messages.format("DataSource.GeneralMethodError", "prepareStatement(String,String[])"), sqle);
            throw sqle;
        } catch (Throwable t) {
            SQLException sqle = createWrappedSQLException(t, "prepareStatement(String,String[])");
            throw sqle;
        }
    }

    public void close() throws SQLException {

        updateLastAccess();
        proxy.clearWarnings();
        if (theStatement != null) {
            theStatement.clearWarnings();
            theStatement.close();
        }
        theStatement = null;
        try {
            proxy.setAutoCommit(true);
        } catch (SQLException sqle) {
        }
        try {
            proxy.setCatalog(catalog);
        } catch (SQLException sqle) {
        }
        try {
            proxy.setHoldability(holdability);
        } catch (SQLException sqle) {
        }
        closeStatements(false);
        try {
            proxy.close();
        } finally {
            proxy = null;
            dataSource.clearConnection();
            dataSource = null;
        }
    }

    public Map<String, Class< ? >> getTypeMap() throws SQLException {

        updateLastAccess();
        try {
            return proxy.getTypeMap();
        } catch (SQLException sqle) {
            error(messages.format("DataSource.GeneralMethodError", "getTypeMap()"), sqle);
            throw sqle;
        } catch (Throwable t) {
            SQLException sqle = createWrappedSQLException(t, "getTypeMap()");
            throw sqle;
        }
    }

    public int getHoldability() throws SQLException {

        updateLastAccess();
        try {
            return proxy.getHoldability();
        } catch (SQLException sqle) {
            error(messages.format("DataSource.GeneralMethodError", "getHoldability()"), sqle);
            throw sqle;
        } catch (Throwable t) {
            SQLException sqle = createWrappedSQLException(t, "getHoldability()");
            throw sqle;
        }
    }

    public String nativeSQL(String sql) throws SQLException {

        updateLastAccess();
        try {
            return proxy.nativeSQL(sql);
        } catch (SQLException sqle) {
            error(messages.format("DataSource.GeneralMethodError", "nativeSQL(String)"), sqle);
            throw sqle;
        } catch (Throwable t) {
            SQLException sqle = createWrappedSQLException(t, "nativeSQL(String)");
            throw sqle;
        }
    }

    public void setAutoCommit(boolean autoCommit) throws SQLException {

        updateLastAccess();
        try {
            // TODO re-implement this
            // JdbcCommandLogger logger = dataSource.getCommandLogger();
            // if (logger != null) {
            // if (!autoCommit) {
            // logger.logTransactionStarted(dataSource);
            // } else {
            // logger.logTransactionFinished(dataSource);
            // }
            // }
            proxy.setAutoCommit(autoCommit);
        } catch (SQLException sqle) {
            error(messages.format("DataSource.GeneralMethodError", "setAutoCommit(boolean)"), sqle);
            throw sqle;
        } catch (Throwable t) {
            SQLException sqle = createWrappedSQLException(t, "setAutoCommit(boolean)");
            throw sqle;
        }
    }

    public boolean getAutoCommit() throws SQLException {

        updateLastAccess();
        try {
            return proxy.getAutoCommit();
        } catch (SQLException sqle) {
            error(messages.format("DataSource.GeneralMethodError", "getAutoCommit()"), sqle);
            throw sqle;
        } catch (Throwable t) {
            SQLException sqle = createWrappedSQLException(t, "getAutoCommit()");
            throw sqle;
        }
    }

    public void commit() throws SQLException {

        updateLastAccess();
        try {
            // TODO re-implement this
            // JdbcCommandLogger logger = dataSource.getCommandLogger();
            // if (logger != null) {
            // logger.logTransactionCommited(dataSource);
            // }
            proxy.commit();
        } catch (SQLException sqle) {
            error(messages.format("DataSource.GeneralMethodError", "commit()"), sqle);
            throw sqle;
        } catch (Throwable t) {
            SQLException sqle = createWrappedSQLException(t, "commit()");
            throw sqle;
        } finally {
            if (!supportsOpenCursorsAcrossCommit()) {
                closeStatements(true);
            }
            if (!supportsOpenStatementsAcrossCommit()) {
                closeStatements(false);
            }
        }
    }

    public void rollback() throws SQLException {

        updateLastAccess();
        try {
            // TODO re-implement this
            // JdbcCommandLogger logger = dataSource.getCommandLogger();
            // if (logger != null) {
            // logger.logTransactionRolledback(dataSource);
            // }
            proxy.rollback();
        } catch (SQLException sqle) {
            error(messages.format("DataSource.GeneralMethodError", "rollback()"), sqle);
            throw sqle;
        } catch (Throwable t) {
            SQLException sqle = createWrappedSQLException(t, "rollback()");
            throw sqle;
        } finally {
            if (!supportsOpenCursorsAcrossRollback()) {
                closeStatements(true);
            }
            if (!supportsOpenStatementsAcrossRollback()) {
                closeStatements(false);
            }
        }
    }

    public boolean isClosed() throws SQLException {

        updateLastAccess();
        try {
            return proxy.isClosed();
        } catch (SQLException sqle) {
            error(messages.format("DataSource.GeneralMethodError", "isClosed()"), sqle);
            throw sqle;
        } catch (Throwable t) {
            SQLException sqle = createWrappedSQLException(t, "isClosed()");
            throw sqle;
        }
    }

    public synchronized DatabaseMetaData getMetaData() throws SQLException {

        updateLastAccess();
        try {
            if (metaData == null) {
                metaData = new DatabaseMetadataWrapper(this, proxy.getMetaData());
            }
            return metaData;
        } catch (SQLException sqle) {
            error(messages.format("DataSource.GeneralMethodError", "getMetaData()"), sqle);
            throw sqle;
        } catch (Throwable t) {
            SQLException sqle = createWrappedSQLException(t, "getMetaData()");
            throw sqle;
        }
    }

    public void setReadOnly(boolean readOnly) throws SQLException {

        updateLastAccess();
        try {
            proxy.setReadOnly(readOnly);
        } catch (SQLException sqle) {
            error(messages.format("DataSource.GeneralMethodError", "setReadOnly(boolean)"), sqle);
            throw sqle;
        } catch (Throwable t) {
            SQLException sqle = createWrappedSQLException(t, "setReadOnly(boolean)");
            throw sqle;
        }
    }

    public boolean isReadOnly() throws SQLException {

        updateLastAccess();
        try {
            return proxy.isReadOnly();
        } catch (SQLException sqle) {
            error(messages.format("DataSource.GeneralMethodError", "isReadOnly()"), sqle);
            throw sqle;
        } catch (Throwable t) {
            SQLException sqle = createWrappedSQLException(t, "isReadOnly()");
            throw sqle;
        }
    }

    public void setCatalog(String catalog) throws SQLException {

        updateLastAccess();
        try {
            proxy.setCatalog(catalog);
        } catch (SQLException sqle) {
            error(messages.format("DataSource.GeneralMethodError", "setCatalog(String)"), sqle);
            throw sqle;
        } catch (Throwable t) {
            SQLException sqle = createWrappedSQLException(t, "setCatalog(String)");
            throw sqle;
        }
    }

    public String getCatalog() throws SQLException {

        updateLastAccess();
        try {
            return proxy.getCatalog();
        } catch (SQLException sqle) {
            error(messages.format("DataSource.GeneralMethodError", "getCatalog()"), sqle);
            throw sqle;
        } catch (Throwable t) {
            SQLException sqle = createWrappedSQLException(t, "getCatalog())");
            throw sqle;
        }
    }

    public void setTypeMap(Map<String, Class< ? >> map) throws SQLException {

        updateLastAccess();
        try {
            proxy.setTypeMap(map);
        } catch (SQLException sqle) {
            error(messages.format("DataSource.GeneralMethodError", "setTypeMap(Map)"), sqle);
            throw sqle;
        } catch (Throwable t) {
            SQLException sqle = createWrappedSQLException(t, "setTypeMap(Map)");
            throw sqle;
        }
    }

    public void setTransactionIsolation(int level) throws SQLException {

        updateLastAccess();
        throw new SQLException(messages.format("DataSource.TransactioIsoError"));
    }

    public int getTransactionIsolation() throws SQLException {

        updateLastAccess();
        try {
            return proxy.getTransactionIsolation();
        } catch (SQLException sqle) {
            error(messages.format("DataSource.GeneralMethodError", "getTransactionIsolation()"), sqle);
            throw sqle;
        } catch (Throwable t) {
            SQLException sqle = createWrappedSQLException(t, "getTransactionIsolation()");
            throw sqle;
        }
    }

    public SQLWarning getWarnings() throws SQLException {

        updateLastAccess();
        try {
            return proxy.getWarnings();
        } catch (SQLException sqle) {
            error(messages.format("DataSource.GeneralMethodError", "getWarnings()"), sqle);
            throw sqle;
        } catch (Throwable t) {
            SQLException sqle = createWrappedSQLException(t, "getWarnings()");
            throw sqle;
        }
    }

    public void clearWarnings() throws SQLException {

        updateLastAccess();
        try {
            proxy.clearWarnings();
        } catch (SQLException sqle) {
            error(messages.format("DataSource.GeneralMethodError", "clearWarnings()"), sqle);
            throw sqle;
        } catch (Throwable t) {
            SQLException sqle = createWrappedSQLException(t, "clearWarnings()");
            throw sqle;
        }
    }

    public void releaseSavepoint(Savepoint savepoint) throws SQLException {

        updateLastAccess();
        try {
            proxy.releaseSavepoint(savepoint);
        } catch (SQLException sqle) {
            error(messages.format("DataSource.GeneralMethodError", "releaseSavepoint(Savepoint)"), sqle);
            throw sqle;
        } catch (Throwable t) {
            SQLException sqle = createWrappedSQLException(t, "releaseSavepoint(Savepoint)");
            throw sqle;
        }
    }

    public void rollback(Savepoint savepoint) throws SQLException {

        updateLastAccess();
        try {
            proxy.rollback(savepoint);
        } catch (SQLException sqle) {
            error(messages.format("DataSource.GeneralMethodError", "rollback(Savepoint)"), sqle);
            throw sqle;
        } catch (Throwable t) {
            SQLException sqle = createWrappedSQLException(t, "rollback(Savepoint)");
            throw sqle;
        }
    }

    public void setHoldability(int holdability) throws SQLException {

        updateLastAccess();
        try {
            proxy.setHoldability(holdability);
        } catch (SQLException sqle) {
            error(messages.format("DataSource.GeneralMethodError", "setHoldability(int)"), sqle);
            throw sqle;
        } catch (Throwable t) {
            SQLException sqle = createWrappedSQLException(t, "setHoldability(int)");
            throw sqle;
        }
    }

    public Savepoint setSavepoint() throws SQLException {

        updateLastAccess();
        try {
            Savepoint savePoint = proxy.setSavepoint();
            return savePoint;
        } catch (SQLException sqle) {
            error(messages.format("DataSource.GeneralMethodError", "setSavePoint()"), sqle);
            throw sqle;
        } catch (Throwable t) {
            SQLException sqle = createWrappedSQLException(t, "setSavepoint()");
            throw sqle;
        }
    }

    public Savepoint setSavepoint(String name) throws SQLException {

        updateLastAccess();
        try {
            Savepoint savePoint = proxy.setSavepoint(name);
            return savePoint;
        } catch (SQLException sqle) {
            error(messages.format("DataSource.GeneralMethodError", "setSavePoint(String)"), sqle);
            throw sqle;
        } catch (Throwable t) {
            SQLException sqle = createWrappedSQLException(t, "setSavepoint(name)");
            throw sqle;
        }
    }

    boolean isIdle(int timeout) {

        long idle = timeout * 1000;
        return (System.currentTimeMillis() - lastAccess) >= idle;
    }

    /**
     * Closes all PreparedStatments and CallableStatements
     */
    void closeStatements(boolean cursorsOnly) throws SQLException {

        boolean throwException = false;
        if (theStatement != null && !cursorsOnly) {
            try {
                theStatement.getParent().close();
            } catch (SQLException sqle) {
                error("Error closing callable statements", sqle);
                throwException = true;
            }
        }
        // for prepared statements
        Enumeration enumer = stmtMap.keys();
        while (enumer.hasMoreElements()) {
            Object key = enumer.nextElement();
            try {
                PreparedStatementWrapper stmt = (PreparedStatementWrapper) stmtMap.get(key);
                stmt.close();
            } catch (SQLException ex) {
                throwException = true;
            } finally {
                stmtMap.remove(key);
            }
        }
        // for callable statements
        enumer = procMap.keys();
        while (enumer.hasMoreElements()) {
            Object key = enumer.nextElement();
            try {
                PreparedStatementWrapper stmt = (PreparedStatementWrapper) stmtMap.get(key);
                if (stmt == null) {
                    continue;
                }
                stmt.close();
            } catch (SQLException ex) {
                throwException = true;
            } finally {
                procMap.remove(key);
            }
        }
        if (throwException) {
            throw new SQLException("Error closing statements");
        }
    }

    /**
     * Returns when this connection was last checked out; 0 if it has never been used.
     */
    long getLastAccess() {

        return lastAccess;
    }

    /**
     * Returns when this connection was last checked out; 0 if it has never been used.
     */
    synchronized void updateLastAccess() throws SQLException {

        if (isClosed) {
            throw new SQLException(messages.format("DataSource.ConnectionClosed"));
        }
        lastAccess = System.currentTimeMillis();
    }

    void add(StatementWrapper statement) {

        openStatements.add(statement);
    }

    void remove(StatementWrapper statement) {

        openStatements.remove(statement);
    }

    SQLException createWrappedSQLException(Throwable t, String method) {

        error(messages.format("DataSource.UnhandledExceptionDuringCall", method), t);
        SQLException sqle = new SQLException(messages.format("DataSource.GeneralMethodError", method));
        BasicUtilities.wrapThrowable(t, sqle);
        return sqle;
    }

    protected void info(Object message, Throwable error) {

        dataSource.info(message, error);
    }

    protected void info(Object message) {

        dataSource.info(message);
    }

    protected void trace(Object message, Throwable error) {

        dataSource.trace(message, error);
    }

    protected void trace(Object message) {

        dataSource.trace(message);
    }

    protected void error(Object message, Throwable error) {

        dataSource.error(message, error);
    }

    protected void error(Object message) {

        dataSource.error(message);
    }

    protected void warn(Object message, Throwable error) {

        dataSource.warn(message, error);
    }

    protected void warn(Object message) {

        dataSource.warn(message);
    }

    protected Connection getConnection() {

        return proxy;
    }

    /*
     * Check to see if the connection has been closed (typically by the server) and attempt to recover if it has.
     */
    protected void guardConnection() throws SQLException {

        boolean badConnection;
        try {
            badConnection = proxy.isClosed();
        } catch (SQLException sqe) {
            badConnection = true;
        }
        if (badConnection) {
            String poolName = null;
            synchronized (this) {
                warn(messages.format("PooledConnection.ConnectionGuardError", poolName));
                this.theStatement = null;
                int retryCount = 0;
                for (retryCount = 0; retryCount < CONN_MAX_RETRY; retryCount++) {
                    try {
                        proxy = dataSource.createDriverConnection(dataSource.getPrincipal(), dataSource
                                .getCredentials());
                    } catch (SQLException SQE) {
                        try {
                            Thread.sleep(CONN_RETRY_INTERVAL);
                        } catch (InterruptedException ie) {
                            Thread.currentThread().interrupt();
                            trace(messages.format("PooledConnection.ConnectionGuardInterrupted", poolName));
                            throw new SQLException(messages.format("PooledConnection.ConnectionClosedUnexpectedly"));
                        }
                        continue;
                    }
                    info(messages.format("PooledConnection.ConnectionGuardSuccess", poolName));
                    return;
                }
            }
        }
    }

    protected boolean supportsStatementPooling() {

        try {
            DatabaseMetaData md = proxy.getMetaData();
            return md.supportsStatementPooling();
        } catch (Throwable t) {
            return false;
        }
    }

    protected boolean supportsOpenCursorsAcrossCommit() {

        try {
            DatabaseMetaData md = proxy.getMetaData();
            return md.supportsOpenCursorsAcrossCommit();
        } catch (Throwable t) {
            return false;
        }
    }

    protected boolean supportsOpenCursorsAcrossRollback() {

        try {
            DatabaseMetaData md = proxy.getMetaData();
            return md.supportsOpenCursorsAcrossRollback();
        } catch (Throwable t) {
            return false;
        }
    }

    protected boolean supportsOpenStatementsAcrossCommit() {

        try {
            DatabaseMetaData md = proxy.getMetaData();
            return md.supportsOpenStatementsAcrossCommit();
        } catch (Throwable t) {
            return false;
        }
    }

    protected boolean supportsOpenStatementsAcrossRollback() {

        try {
            DatabaseMetaData md = proxy.getMetaData();
            return md.supportsOpenStatementsAcrossRollback();
        } catch (Throwable t) {
            return false;
        }
    }
}
